home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
GUSI
/
GUSIAppleTalk.cp
< prev
next >
Wrap
Text File
|
1993-12-30
|
18KB
|
924 lines
/*********************************************************************
Project : GUSI - Grand Unified Socket Interface
File : GUSIAppleTalk.cp - Appletalk Sockets
Author : Matthias Neeracher
Started : 03May92 Language : MPW C/C++
Modified : 10May92 MN ADSPStreams
12May92 MN NBP stuff
18May92 MN Basic functions work
18May92 MN Out of band data
13Jul92 MN Make AppleTalkSockets available to other socket classes.
21Jul92 MN Support symbolic addresses
26Jul92 MN Error in using memccpy()
28Jul92 MN Separate creating symaddrs from registering them
10Aug92 MN Improve select()
07Sep92 MN Implement ioctl()
13Sep92 MN Always complete write
05Oct92 MN I was a teenage NBP werewolf
07Dec92 MN Use flags
17Jan93 MN Handle user interrupts more carefully.
07Feb93 MN New configuration technique
01Sep93 MN Throw out nonbreaking spaces
17Nov93 MN Delay opening AppleTalk services
30Dec93 MN Fiddle with select()
Last : 30Dec93
*********************************************************************/
#include "GUSI_P.h"
#include <Errors.h>
#include <ADSP.h>
#include <Devices.h>
#include <GestaltEqu.h>
#include <PLStringFuncs.h>
#include <Strings.h>
#include <sys/types.h>
class AtlkSymAddr;
class AppleTalkSocketDomain : public SocketDomain {
short dspRefNum;
void DoMPPOpen();
public:
AppleTalkSocketDomain();
AddrBlock node;
short GetDSP();
Boolean Validate();
virtual Socket * socket(int type, short protocol);
virtual int choose(
int type,
char * prompt,
void * constraint,
int flags,
void * name,
int * namelen);
};
class AppleTalkSocket : public Socket { // That's what this file's all about
friend class AppleTalkSocketDomain;
protected:
Boolean nonblocking;
Boolean ownSocket;
Boolean readShutDown;
Boolean writeShutDown;
u_char socket;
AddrBlock peer;
AtlkSymAddr * symaddr;
AppleTalkSocket(u_char sock = 0);
virtual ~AppleTalkSocket();
public:
virtual int bind(void * name, int namelen);
virtual int getsockname(void * name, int * namelen);
virtual int getpeername(void *name, int *namelen);
virtual int fcntl(unsigned int cmd, int arg);
virtual int ioctl(unsigned int request, void *argp);
};
struct ADSPSockBuffers {
enum {qSize = 4150};
u_char attnBuf[attnBufSize];
u_char sendBuf[qSize];
u_char recvBuf[qSize];
};
typedef ADSPSockBuffers * ADSPBufPtr;
class ADSPSocket : public AppleTalkSocket {
friend class AppleTalkSocketDomain;
ADSPSocket(u_char sock = 0);
TRCCB * ccb;
DSPPBPtr pb;
ADSPBufPtr bufs;
int Init();
void UnInit(Boolean abort);
Boolean Waiting();
Boolean Up();
public:
virtual int listen(int qlen);
virtual int connect(void * address, int addrlen);
virtual Socket * accept(void * address, int * addrlen);
virtual int recvfrom(void * buffer, int buflen, int flags, void * from, int * fromlen);
virtual int sendto(void * buffer, int buflen, int flags, void * to, int tolen);
virtual int shutdown(int how);
virtual int ioctl(unsigned int request, void *argp);
virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
virtual ~ADSPSocket();
};
class AtlkSymAddr {
struct NTE {
Ptr next;
AddrBlock addr;
char rsrv;
char name;
};
NTE * nte;
Boolean legit;
public:
AtlkSymAddr(const EntityName & name);
~AtlkSymAddr();
void Register(u_char socket);
};
int AtlkLookup(const EntityName & name, AddrBlock * addr);
AppleTalkSocketDomain AppleTalkSockets;
const AddrBlock NoFilter = {0, 0, 0};
/************************ AppleTalkSocket members ************************/
AppleTalkSocket::AppleTalkSocket(u_char sock)
{
socket = sock;
ownSocket = !sock;
nonblocking = false;
readShutDown = false;
writeShutDown = false;
symaddr = nil;
peer = NoFilter;
}
AppleTalkSocket::~AppleTalkSocket()
{
if (socket && ownSocket) {
MPPParamBlock mpp;
mpp.DDP.socket = socket;
PCloseSkt(&mpp, false);
}
if (symaddr)
delete symaddr;
}
int AppleTalkSocket::fcntl(unsigned int cmd, int arg)
{
switch (cmd) {
case F_GETFL:
if (nonblocking)
return FNDELAY;
else
return 0;
case F_SETFL:
if (arg & FNDELAY)
nonblocking = true;
else
nonblocking = false;
return 0;
default:
return GUSI_error(EOPNOTSUPP);
}
}
int AppleTalkSocket::ioctl(unsigned int request, void *argp)
{
switch (request) {
case FIONBIO:
nonblocking = (Boolean) *(long *) argp;
return 0;
default:
return GUSI_error(EOPNOTSUPP);
}
}
int AppleTalkSocket::bind(void *sa_name, int)
{
switch (*(short *) sa_name) {
case AF_APPLETALK:
{
struct sockaddr_atlk * addr = (struct sockaddr_atlk *) sa_name;
if (socket || !addr->addr.aSocket)
return GUSI_error(EINVAL);
socket = addr->addr.aSocket;
ownSocket = false;
}
break;
case ATALK_SYMADDR:
{
struct sockaddr_atlk_sym * addr = (struct sockaddr_atlk_sym *) sa_name;
symaddr = new AtlkSymAddr(addr->name);
if (errno) {
delete symaddr;
symaddr = nil;
return -1;
}
}
break;
default:
return GUSI_error(EAFNOSUPPORT);
}
return 0;
}
int AppleTalkSocket::getsockname(void *name, int *namelen)
{
struct sockaddr_atlk addr;
addr.family = AF_APPLETALK;
addr.addr = AppleTalkSockets.node;
addr.addr.aSocket = socket;
memcpy(name, &addr, *namelen = min(*namelen, sizeof(struct sockaddr_atlk)));
return 0;
}
int AppleTalkSocket::getpeername(void *name, int *namelen)
{
struct sockaddr_atlk addr;
if (!peer.aNet && !peer.aNode && !peer.aSocket)
return GUSI_error(ENOTCONN);
addr.family = AF_APPLETALK;
addr.addr = peer;
memcpy(name, &addr, *namelen = min(*namelen, sizeof(struct sockaddr_atlk)));
return 0;
}
/********************* ADSPSocket members *********************/
ADSPSocket::ADSPSocket(u_char sock)
: AppleTalkSocket(sock)
{
ccb = nil;
pb = nil;
bufs = nil;
if (!AppleTalkSockets.GetDSP())
GUSI_error(EPFNOSUPPORT); // Just an educated guess
}
ADSPSocket::~ADSPSocket()
{
UnInit(false);
}
inline Boolean ADSPSocket::Waiting()
{
return pb->ioResult == 1 && !(ccb->userFlags & (eClosed | eTearDown));
}
inline Boolean ADSPSocket::Up()
{
return !(ccb->userFlags & (eClosed | eTearDown));
}
int ADSPSocket::listen(int)
{
if (ccb)
return GUSI_error(EISCONN);
ccb = new TRCCB;
if (!ccb)
goto memErrCCB;
pb = new DSPParamBlock;
if (!pb)
goto memErrPB;
pb->ioCRefNum = AppleTalkSockets.GetDSP();
pb->csCode = dspCLInit;
pb->u.initParams.ccbPtr = ccb;
pb->u.initParams.localSocket = socket;
if (PBControlSync(ParmBlkPtr(pb)))
goto memErr;
socket = pb->u.initParams.localSocket;
if (symaddr)
symaddr->Register(socket);
pb->csCode = dspCLListen;
pb->ioCompletion = nil;
pb->u.openParams.filterAddress = NoFilter;
PBControlAsync(ParmBlkPtr(pb));
return 0;
memErr:
delete pb;
pb = nil;
memErrPB:
delete ccb;
ccb = nil;
memErrCCB:
return GUSI_error(ENOMEM);
}
int ADSPSocket::Init()
{
if (ccb)
if (pb->csCode == dspOpen && pb->ioResult && pb->ioResult != 1)
return 0; // Second chance for lose on open
else
return GUSI_error(EISCONN); // Got a live un', don't reconnect
ccb = new TRCCB;
if (!ccb)
goto memErrCCB;
pb = new DSPParamBlock;
if (!pb)
goto memErrPB;
bufs = new ADSPSockBuffers;
if (!bufs)
goto memErrBufs;
pb->ioCRefNum = AppleTalkSockets.GetDSP();
pb->csCode = dspInit;
pb->u.initParams.ccbPtr = ccb;
pb->u.initParams.userRoutine = nil;
pb->u.initParams.sendQSize = bufs->qSize;
pb->u.initParams.sendQueue = bufs->sendBuf;
pb->u.initParams.recvQSize = bufs->qSize;
pb->u.initParams.recvQueue = bufs->recvBuf;
pb->u.initParams.attnPtr = bufs->attnBuf;
pb->u.initParams.localSocket = socket;
if (!PBControlSync(ParmBlkPtr(pb))) {
socket = pb->u.initParams.localSocket;
if (symaddr)
symaddr->Register(socket);
return 0;
}
delete bufs;
bufs = nil;
memErrBufs:
delete pb;
pb = nil;
memErrPB:
delete ccb;
ccb = nil;
memErrCCB:
return GUSI_error(ENOMEM);
}
void ADSPSocket::UnInit(Boolean abort)
{
if (ccb && pb) {
pb->csCode = bufs ? dspRemove : dspCLRemove;
pb->u.closeParams.abort = abort;
PBControlSync(ParmBlkPtr(pb));
}
if (ccb)
delete ccb;
if (pb)
delete pb;
if (bufs)
delete bufs;
ccb = nil;
pb = nil;
bufs = nil;
}
int ADSPSocket::connect(void *sa_name, int)
{
switch (*(short *) sa_name) {
case AF_APPLETALK:
{
sockaddr_atlk * addr = (struct sockaddr_atlk *) sa_name;
peer = addr->addr;
}
break;
case ATALK_SYMADDR:
{
struct sockaddr_atlk_sym * addr = (struct sockaddr_atlk_sym *) sa_name;
if (AtlkLookup(addr->name, &peer))
return -1;
}
break;
default:
return GUSI_error(EAFNOSUPPORT);
}
if (Init())
return -1;
pb->csCode = dspOpen;
pb->ioCompletion = nil;
pb->u.openParams.remoteAddress = peer;
pb->u.openParams.filterAddress = NoFilter;
pb->u.openParams.ocMode = ocRequest;
pb->u.openParams.ocInterval = 0;
pb->u.openParams.ocMaximum = 0;
PBControlAsync(ParmBlkPtr(pb));
if (nonblocking)
return GUSI_error(EINPROGRESS);
SAFESPIN(Waiting(), SP_MISC, 0);
if (errno) {
UnInit(true);
return -1;
} else if (pb->ioResult == noErr) {
return 0;
} else
return GUSI_error(ECONNREFUSED);
}
Socket * ADSPSocket::accept(void * address, int * addrlen)
{
ADSPSocket * newsock;
sockaddr_atlk addr;
if (!pb || pb->csCode != dspCLListen)
return (Socket *) GUSI_error_nil(ENOTCONN);
if (nonblocking && pb->ioResult == 1)
return (Socket *) GUSI_error_nil(EWOULDBLOCK);
SPINP(Waiting(), SP_MISC, 0);
if (pb->ioResult)
return (Socket *) GUSI_error_nil(EFAULT);
newsock = new ADSPSocket(socket);
if (!newsock)
return (Socket *) GUSI_error_nil(ENOMEM);
if (newsock->Init())
return (Socket *) nil;
newsock->pb->csCode = dspOpen;
newsock->pb->ioCompletion = nil;
newsock->pb->u.openParams = pb->u.openParams;
newsock->pb->u.openParams.ocMode = ocAccept;
newsock->pb->u.openParams.ocInterval = 0;
newsock->pb->u.openParams.ocMaximum = 0;
PBControlAsync(ParmBlkPtr(newsock->pb));
SAFESPIN(newsock->Waiting(), SP_MISC, 0);
pb->csCode = dspCLListen;
pb->ioCompletion = nil;
pb->u.openParams.filterAddress = NoFilter;
PBControlAsync(ParmBlkPtr(pb));
if (errno || newsock->pb->ioResult) {
delete newsock;
return (Socket *) (errno ? nil : GUSI_error_nil(ECONNREFUSED));
}
if (address && addrlen) {
addr.family = AF_APPLETALK;
addr.addr = pb->u.openParams.remoteAddress;
memcpy(address, &addr, *addrlen = min(*addrlen, sizeof(sockaddr_atlk)));
}
return newsock;
}
int ADSPSocket::recvfrom(void * buffer, int buflen, int flags, void * from, int *)
{
if (from)
return GUSI_error(EOPNOTSUPP);
if (flags & ~MSG_OOB)
return GUSI_error(EOPNOTSUPP);
if (!pb)
return GUSI_error(ENOTCONN);
if (pb->csCode == dspOpen && pb->ioResult) {
if (pb->ioResult == 1) {
if (nonblocking)
return GUSI_error(EWOULDBLOCK);
SPIN(Waiting(), SP_MISC, 0);
}
if (pb->ioResult)
return GUSI_error(ECONNREFUSED);
}
if (flags & MSG_OOB)
if (ccb->userFlags & eAttention) {
memcpy(Ptr(buffer), ccb->attnPtr, buflen = min(buflen, ccb->attnSize));
ccb->userFlags ^= eAttention;
return buflen;
} else
return GUSI_error(EINVAL);
pb->csCode = dspStatus;
PBControlSync(ParmBlkPtr(pb));
if (!pb->u.statusParams.recvQPending)
if (nonblocking)
return GUSI_error(EWOULDBLOCK);
else if (readShutDown)
return 0;
while (!pb->u.statusParams.recvQPending && Up()) {
SPIN(0, SP_STREAM_READ, 0);
PBControlSync(ParmBlkPtr(pb));
}
pb->csCode = dspRead;
pb->u.ioParams.reqCount = min(buflen, pb->u.statusParams.recvQPending);
pb->u.ioParams.dataPtr = (u_char *) buffer;
if (PBControlSync(ParmBlkPtr(pb)))
readShutDown = true;
return pb->u.ioParams.actCount;
}
int ADSPSocket::sendto(void * buffer, int buflen, int flags, void * to, int)
{
if (to)
return GUSI_error(EOPNOTSUPP);
if (flags & ~MSG_OOB)
return GUSI_error(EOPNOTSUPP);
if (!pb)
return GUSI_error(ENOTCONN);
if (pb->csCode == dspOpen && (pb->ioResult || !Up())) {
if (Waiting()) {
if (nonblocking)
return GUSI_error(EWOULDBLOCK);
SPIN(Waiting(), SP_MISC, 0);
}
if (pb->ioResult)
return GUSI_error(ECONNREFUSED);
}
if (writeShutDown)
return GUSI_error(ESHUTDOWN);
if (flags & MSG_OOB) {
if (buflen < 0 || buflen > 570)
return GUSI_error(EINVAL);
pb->csCode = dspAttention;
pb->u.attnParams.attnCode = 0;
pb->u.attnParams.attnSize = buflen;
pb->u.attnParams.attnData = (unsigned char *) buffer;
PBControlSync(ParmBlkPtr(pb));
if (pb->ioResult)
return GUSI_error(EINVAL);
else
return buflen;
}
if (nonblocking) {
pb->csCode = dspStatus;
PBControlSync(ParmBlkPtr(pb));
if (!pb->u.statusParams.sendQFree)
return GUSI_error(EWOULDBLOCK);
}
pb->csCode = dspWrite;
pb->u.ioParams.reqCount =
nonblocking
? min(buflen, pb->u.statusParams.sendQFree)
: buflen;
pb->u.ioParams.dataPtr = (u_char *) buffer;
pb->u.ioParams.eom = false;
pb->u.ioParams.flush = true;
PBControlAsync(ParmBlkPtr(pb));
SPIN(Waiting(), SP_STREAM_WRITE, 0);
if (pb->ioResult)
writeShutDown = true;
return pb->u.ioParams.actCount;
}
int ADSPSocket::shutdown(int how)
{
if (how < 0 || how > 2)
return GUSI_error(EINVAL);
if (how)
writeShutDown = true;
if (!(how & 1))
readShutDown = true;
return 0;
}
int ADSPSocket::ioctl(unsigned int request, void *argp)
{
switch (request) {
case FIONREAD:
if (!pb)
return GUSI_error(ENOTCONN);
pb->csCode = dspStatus;
PBControlSync(ParmBlkPtr(pb));
*(unsigned long *) argp = pb->u.statusParams.recvQPending;
return 0;
default:
return AppleTalkSocket::ioctl(request, argp);
}
}
int ADSPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean * exception)
{
int goodies = 0;
if (pb) {
pb->csCode = dspStatus;
PBControlSync(ParmBlkPtr(pb));
}
if (canRead)
if ( !pb || readShutDown || !Up() || pb->u.statusParams.recvQPending
|| (pb->csCode == dspCLListen && pb->ioResult != 1)
) {
*canRead = true;
++goodies;
}
if (canWrite)
if ( !pb || writeShutDown || !Up() || pb->u.statusParams.sendQFree != 0
|| (pb->csCode == dspOpen && pb->ioResult != 1)
) {
*canWrite = true;
++goodies;
}
if (exception && (ccb->userFlags & eAttention)) {
*exception = true;
++goodies;
}
return goodies;
}
/********************* AppleTalkSocketDomain members **********************/
extern "C" void GUSIwithAppleTalkSockets()
{
AppleTalkSockets.DontStrip();
}
AppleTalkSocketDomain::AppleTalkSocketDomain()
: SocketDomain(AF_APPLETALK)
{
dspRefNum = 0;
node.aNet = -1;
node.aNode = 0;
node.aSocket= 0;
}
void AppleTalkSocketDomain::DoMPPOpen()
{
short myNode;
short myNet;
if (AppleTalkIdentity(myNet, myNode)) {
node.aNet = -1;
node.aNode = 0;
node.aSocket= 0;
} else {
node.aNet = myNet;
node.aNode = (u_char) myNode;
node.aSocket= 1;
}
}
Boolean AppleTalkSocketDomain::Validate()
{
if (!node.aSocket)
DoMPPOpen();
return node.aSocket != 0;
}
short AppleTalkSocketDomain::GetDSP()
{
if (!dspRefNum)
if (OpenDriver("\p.DSP", &dspRefNum))
dspRefNum = 0;
return dspRefNum;
}
Socket * AppleTalkSocketDomain::socket(int type, short)
{
AppleTalkSocket * sock = nil;
errno = 0;
if (!Validate())
GUSI_error(ENETDOWN);
else
switch (type) {
case SOCK_STREAM:
sock = new ADSPSocket();
break;
default:
GUSI_error(ESOCKTNOSUPPORT);
}
if (sock && errno) {
delete sock;
return nil;
} else
return sock;
}
int AppleTalkSocketDomain::choose(int, char * prompt, void * constraint, int flags, void * name, int * namelen)
{
sa_constr_atlk * constr = (sa_constr_atlk *) constraint;
Point where;
NBPReply reply;
sockaddr_atlk addr;
char * end;
Str255 promp;
NLType dummy;
if (!hasStdNBP)
return GUSI_error(EOPNOTSUPP);
if (!Validate())
return GUSI_error(ENETDOWN);
if (flags & (CHOOSE_NEW | CHOOSE_DIR))
return GUSI_error(EINVAL);
SetPt(&where, 100, 100);
memset(&reply.theEntity, 0, sizeof(EntityName));
memcpy(&reply.theEntity.zoneStr, "\p*", 2);
end = (char *) memccpy(promp+1, prompt, 0, 254);
promp[0] = end-(char *)promp-2;
if (
StandardNBP(
where,
promp,
constr ? constr->numTypes : -1,
constr ? constr->types : dummy,
(NameFilterProcPtr) nil,
(ZoneFilterProcPtr) nil,
(DlgHookProcPtr) nil,
&reply)
!= nlOk
)
return GUSI_error(EINTR);
addr.family = AF_APPLETALK;
addr.addr = reply.theAddr;
memcpy(name, &addr, *namelen = min(*namelen, sizeof(sockaddr_atlk)));
return 0;
}
/*********************** AtlkSymAddr members ************************/
static int EntityLen(const EntityName & name)
{
Ptr nm = Ptr(&name);
int l1 = *nm+1;
nm += l1;
int l2 = *nm+1;
nm += l2;
int l3 = *nm+1;
return l1+l2+l3;
}
AtlkSymAddr::AtlkSymAddr(const EntityName & name)
{
int len = EntityLen(name);
errno = 0;
if (!AppleTalkSockets.Validate()) {
GUSI_error(ENETDOWN);
return;
}
nte = (NTE *) NewPtr(9+len);
legit = false;
if (!nte) {
GUSI_error(ENOMEM);
return;
}
nte->next = nil;
memcpy(&nte->name, &name, len);
}
void AtlkSymAddr::Register(u_char socket)
{
MPPParamBlock mpp;
errno = 0;
nte->addr.aSocket = socket;
mpp.NBPinterval = 8;
mpp.NBPcount = 3;
mpp.NBPntQElPtr = Ptr(nte);
mpp.NBPverifyFlag = true;
PRegisterName(&mpp, false);
if (mpp.MPPioResult) {
DisposPtr(Ptr(nte));
GUSI_error((mpp.MPPioResult == nbpDuplicate) ? EADDRINUSE : EFAULT);
} else
legit = true;
}
AtlkSymAddr::~AtlkSymAddr()
{
if (nte) {
if (legit) {
MPPParamBlock mpp;
mpp.NBPentityPtr = Ptr(&nte->name);
PRemoveName(&mpp, false);
}
DisposPtr(Ptr(nte));
}
}
static int AtlkLookup(const EntityName & name, AddrBlock * addr)
{
EntityName ent;
char found[256];
MPPParamBlock mpp;
mpp.NBPinterval = 8;
mpp.NBPcount = 3;
mpp.NBPentityPtr = Ptr(&name);
mpp.NBPretBuffPtr = found;
mpp.NBPretBuffSize = 256;
mpp.NBPmaxToGet = 1;
PLookupName(&mpp, false);
if (!mpp.MPPioResult)
NBPExtract(found, mpp.NBPnumGotten, 1, &ent, addr);
if (mpp.MPPioResult)
return GUSI_error(EADDRNOTAVAIL);
return 0;
}